﻿<template>
  <div class="satellite-scheduling">
    <div class="scheduling-header">
      <h2>整星排程</h2>
      <div class="header-controls">
        <button class="btn btn-success save-btn" @click="manualSave" :disabled="isSaving">
          <span v-if="isSaving">保存中...</span>
          <span v-else>💾 保存</span>
        </button>
        
        <div class="date-picker-section">
          <div class="date-picker-container">
            <input type="date" v-model="startDate" class="date-input" placeholder="选择起始日期">
            <div class="day-counter">
              <button class="counter-btn" @click="decrementDays">-</button>
              <input type="number" v-model.number="dayCount" class="day-input" min="1" max="365">
              <button class="counter-btn" @click="incrementDays">+</button>
            </div>
          </div>
        </div>
        
        <div class="action-buttons">
          <button class="btn btn-success query-device-btn" @click="showDeviceQuery">设备资源冲突查询</button>
          <button class="btn btn-primary" @click="showDeviceMaintenance">设备资源维护</button>
          <button class="btn btn-info" @click="showTaskConfig">任务块定义配置</button>
          <button class="btn btn-secondary" @click="showTemplateConfig">模板配置</button>
        </div>
      </div>
    </div>

    <div class="scheduling-content">
      <div v-if="!hasScheduleData" class="empty-state">
        <div class="empty-icon">🛰️</div>
        <p class="empty-text">暂无排程数据</p>
        <button class="btn btn-primary" @click="startScheduling">开始排程</button>
      </div>

      <div v-else class="schedule-grid">
        <!-- 表格格式的排程视图 -->
        <div class="schedule-table" 
             @mousedown="onScheduleTableMouseDown($event)"
             @mousemove="onScheduleTableMouseMove($event)"
             @mouseup="onScheduleTableMouseUp($event)"
             @mouseleave="onScheduleTableMouseLeave($event)">
          <table class="scheduling-table">
            <thead>
              <tr>
                <th class="time-header">时间</th>
                <th v-for="satellite in allSatellites" :key="satellite.id" 
                    class="satellite-header clickable-header"
                    :class="{ 'highlighted-column': isColumnHighlighted(satellite.id) }"
                    @click="showTemplateSelector(satellite)">
                  {{ satellite.name }}
                </th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="(timeSlot, timeIndex) in timeSlots" :key="timeIndex" 
                  class="schedule-row"
                  :class="{ 'highlighted-row': isRowHighlighted(timeIndex) }">
                <td class="time-cell"
                    :class="{ 'highlighted-row': isRowHighlighted(timeIndex) }">
                  <div class="time-display">
                    {{ formatTimeSlot(timeSlot) }}
                  </div>
                </td>
                <td v-for="satellite in allSatellites" :key="satellite.id" 
                    class="schedule-cell"
                    :class="{ 
                      'highlighted-cell': isCellHighlighted(satellite.id, timeIndex),
                      'highlighted-column': isColumnHighlighted(satellite.id),
                      'highlighted-row': isRowHighlighted(timeIndex)
                    }"
                    @click="handleCellClick($event, satellite.id, timeIndex)"
                    @dragover="onCellDragOver($event, satellite.id, timeIndex)"
                    @drop="onCellDrop($event, satellite.id, timeIndex)"
                    @dragenter="onCellDragEnter($event, satellite.id, timeIndex)"
                    @dragleave="onCellDragLeave($event, satellite.id, timeIndex)"
                    @contextmenu.prevent="showCellContextMenu($event, satellite.id, timeIndex)">
                  <div class="schedule-content">
                    <!-- 整体任务块显示，支持按天冲突标记 -->
                    <div v-for="(item, index) in getScheduleItemsAtStart(satellite.id, timeIndex)" 
                         :key="item.id" 
                         class="schedule-item"
                         :class="[item.type, { 
                           'selected': isItemSelected(item.id), 
                           'single-day': item.duration === 1,
                           'has-conflict': item.conflictDays && item.conflictDays.length > 0
                         }]"
                         :style="{ 
                           backgroundColor: item.color, 
                           color: '#fff',
                           height: (getVisibleDuration(item, timeIndex) * 13) + 'px',
                           position: 'absolute',
                           top: '0',
                           left: '0',
                           right: '0',
                           zIndex: 10,
                           pointerEvents: 'auto',
                           minHeight: (getVisibleDuration(item, timeIndex) * 13) + 'px',
                           maxHeight: (getVisibleDuration(item, timeIndex) * 13) + 'px',
                           width: '100%'
                         }"
                         draggable="true"
                         :data-task-id="item.id"
                         @click="handleItemClick($event, item)"
                         @dblclick="handleItemDoubleClick($event, item)"
                         @dragstart="onScheduleItemDragStart($event, item, index, satellite.id)"
                         @dragend="onScheduleItemDragEnd($event, item)"
                         @contextmenu.prevent="showContextMenu($event, item)"
                         @mouseenter="onScheduleItemMouseEnter($event, item)"
                         @mousemove="onScheduleItemMouseMove($event)"
                         @mouseleave="onScheduleItemMouseLeave()">
                      
                      <!-- 任务块内容 -->
                      <div class="schedule-item-content">
                        <div class="schedule-item-title">{{ item.title }}</div>
                      </div>
                      
                      <!-- 冲突天数的红色标记覆盖层 -->
                      <div v-if="item.conflictDays && item.conflictDays.length > 0" class="conflict-overlay">
                        <div v-for="dayIndex in item.conflictDays" 
                             :key="`conflict-${item.id}-${dayIndex}`"
                             class="conflict-day-mark"
                             :style="{ 
                               position: 'absolute',
                               top: (dayIndex * 13) + 'px',
                               left: '0',
                               right: '0',
                               height: '13px',
                               backgroundColor: 'rgba(220, 53, 69, 0.8)',
                               pointerEvents: 'none',
                               zIndex: 1
                             }">
                        </div>
                      </div>
                    </div>
                  </div>
                </td>
              </tr>
            </tbody>
          </table>
          
          <!-- 框选区域 -->
          <div v-if="isSelecting" 
               class="selection-box"
               :style="{
                 left: selectionBox.left + 'px',
                 top: selectionBox.top + 'px',
                 width: selectionBox.width + 'px',
                 height: selectionBox.height + 'px'
               }">
          </div>
        </div>
      </div>
    </div>
    
    <!-- 设备资源维护弹窗 -->
    <DeviceResourceMaintenance 
      :visible="deviceMaintenanceVisible"
      @close="deviceMaintenanceVisible = false"
      @save="handleDeviceSave"
    />
    
    <!-- 任务块定义配置弹窗 -->
    <TaskBlockConfiguration 
      :visible="taskConfigVisible"
      :available-devices="availableDevices"
      @close="taskConfigVisible = false"
      @save="handleTaskSave"
    />

    <!-- 模板配置弹窗 -->
    <TemplateConfiguration 
      :visible="templateConfigVisible"
      @close="templateConfigVisible = false"
      @templates-updated="handleTemplatesUpdated"
    />

    <!-- 设备查询甘特图弹窗 -->
    <DeviceQueryGantt 
      :visible="deviceQueryVisible"
      @close="deviceQueryVisible = false"
      :scheduleItems="scheduleItems"
      :satellites="allSatellites"
      :startDate="startDate"
      :dayCount="dayCount"
      :availableDevices="availableDevices"
    />
    
    <!-- 右键上下文菜单 -->
    <div v-if="contextMenuVisible" class="context-menu" :style="{ left: contextMenuX + 'px', top: contextMenuY + 'px' }" @click.stop>
      <div class="context-menu-item" @click="showTaskBlockSelector">
        <span>添加</span>
      </div>
      <div v-if="hasTaskBlockAtContextPosition" class="context-menu-item" @click="deleteScheduleItem">
        <span>删除</span>
      </div>
    </div>
    
    <!-- 任务块编辑弹窗 -->
    <div v-if="editItemVisible" class="modal-overlay" @click="closeEditItem">
      <div class="modal-content edit-item-modal" @click.stop>
        <div class="modal-header">
          <h3>
            {{ editingItem.id === 'multiple' ? `批量编辑任务块 (${editingItem.selectedItemsCount}个)` : '编辑任务块' }}
          </h3>
          <button class="close-btn" @click="closeEditItem">×</button>
        </div>
        
        <div class="modal-body">
          <div class="form-group">
            <label>任务名称:</label>
            <input v-model="editingItem.title" type="text" class="form-input" 
                   :placeholder="editingItem.id === 'multiple' ? '输入新名称（将应用到所有选中的任务块）' : '请输入任务名称'">
            <div v-if="editingItem.id === 'multiple'" class="form-hint">
              注意：修改任务名称将同时应用到所有 {{ editingItem.selectedItemsCount }} 个选中的任务块
            </div>
          </div>
          
          <div class="form-group">
            <label>持续时间:</label>
            <input v-model.number="editingItem.duration" type="number" min="1" class="form-input" placeholder="天数">
          </div>
          
          <div class="form-group">
            <label>需要设备（总体）:</label>
            <div class="device-selector">
              <!-- 已选设备展示框 -->
              <div class="selected-devices-display" @click="toggleDeviceDropdown">
                <div v-if="editingItem.requiredDevices.length === 0" class="placeholder-text">
                  请选择设备...
                </div>
                <div v-else class="selected-devices">
                  <span v-for="device in editingItem.requiredDevices" :key="device" class="device-tag">
                    {{ device }}
                    <button type="button" class="remove-device" @click.stop="removeDevice(device)">×</button>
                  </span>
                </div>
                <button type="button" class="dropdown-toggle" @click.stop="toggleDeviceDropdown">▼</button>
              </div>
              
              <!-- 下拉选择框 -->
              <div v-show="showDeviceDropdown" class="device-dropdown">
                <div class="device-group">
                  <div class="group-title">试验中心</div>
                  <div v-for="device in availableDevices.testCenter" :key="device.id" class="device-option" @click="toggleDeviceSelection(device.name)">
                    <input type="checkbox" :checked="editingItem.requiredDevices.includes(device.name)" readonly>
                    <span>{{ device.name }}</span>
                  </div>
                </div>
                <div class="device-group">
                  <div class="group-title">总装</div>
                  <div v-for="device in availableDevices.assembly" :key="device.id" class="device-option" @click="toggleDeviceSelection(device.name)">
                    <input type="checkbox" :checked="editingItem.requiredDevices.includes(device.name)" readonly>
                    <span>{{ device.name }}</span>
                  </div>
                </div>
              </div>
            </div>
          </div>
          
          <div class="form-group" v-if="editingItem.requiredDevices.length > 0">
            <label>设备容量:</label>
            <div class="device-capacity-config">
              <div v-for="device in editingItem.requiredDevices" :key="device" class="capacity-item">
                <label class="device-label">{{ device }}:</label>
                <input 
                  type="number" 
                  min="1" 
                  class="capacity-input"
                  :value="editingItem.deviceCapacities[device] || getDefaultDeviceCapacity(device)"
                  @input="updateDeviceCapacity(device, $event.target.value)"
                  :placeholder="getDefaultDeviceCapacity(device)"
                />
                <span class="capacity-unit">个</span>
              </div>
            </div>
          </div>
          
          <div class="form-group">
            <div class="day-configs-header">
              <label>每日设备配置:</label>
              <div class="button-group">
                <button 
                  class="activate-all-btn" 
                  @click="activateAllDaysDevices"
                  :disabled="editingItem.requiredDevices.length === 0"
                  title="为所有天数激活总体所需设备"
                >
                  全部激活
                </button>
                <button 
                  class="clear-all-btn" 
                  @click="clearAllDaysDevices"
                  title="清除所有天数的设备配置"
                >
                  全部删除
                </button>
              </div>
            </div>
            <div class="day-configs">
              <div v-for="day in editingItem.duration" :key="day" class="day-config-row">
                <span class="day-label">第{{ day }}天:</span>
                <div class="day-device-control">
                  <!-- 显示当前激活的设备 -->
                  <div class="active-devices" v-if="editingItem.dayConfigs[day-1] && editingItem.dayConfigs[day-1].length > 0">
                    <span v-for="device in editingItem.dayConfigs[day-1]" :key="device" class="device-tag">
                      {{ device }}
                      <button class="remove-device" @click="removeDayDevice(day-1, device)">×</button>
                    </span>
                  </div>
                  <div class="no-devices" v-else>
                    未激活任何设备
                  </div>
                  
                  <!-- 激活按钮 -->
                  <button 
                    class="activate-btn" 
                    @click="activateDayDevices(day-1)"
                    :disabled="editingItem.requiredDevices.length === 0"
                  >
                    {{ editingItem.dayConfigs[day-1] && editingItem.dayConfigs[day-1].length > 0 ? '重新激活' : '激活设备' }}
                  </button>
                </div>
              </div>
            </div>
          </div>
        </div>
        
        <div class="modal-footer">
          <button class="cancel-btn" @click="closeEditItem">取消</button>
          <button class="save-btn" @click="saveEditItem">保存</button>
        </div>
      </div>
    </div>

    <!-- 右键上下文菜单 -->
    <div v-if="templateDesignVisible" class="modal-overlay" @click="closeTemplateDesign">
      <div class="modal-content template-design-modal" @click.stop :style="templateModalStyle">
        <div class="modal-header draggable-header" 
             @mousedown="startDragTemplateModal"
             @mousemove="dragTemplateModal"
             @mouseup="stopDragTemplateModal">
          <h3>{{ editingTemplateId ? '编辑模板' : '创建模板' }}</h3>
          <button class="close-btn" @click="closeTemplateDesign">×</button>
        </div>
        
        <div class="template-design-body">
          <div class="task-blocks-panel">
            <div class="panel-header">
              <h4>任务块</h4>
            </div>
            <div class="task-blocks-list">
              <div 
                v-for="taskBlock in availableTaskBlocks" 
                :key="taskBlock.id"
                class="task-block-item"
                draggable="true"
                @dragstart="onDragStart($event, taskBlock)"
                :style="{ backgroundColor: taskBlock.color }"
              >
                <div class="task-block-name">{{ taskBlock.name || '未命名任务' }}</div>
                <div class="task-block-duration">{{ taskBlock.duration }}天</div>
                <div v-if="taskBlock.requiredDevice" class="task-block-device">设备: {{ taskBlock.requiredDevice }}</div>
              </div>
              
              <div v-if="availableTaskBlocks.length === 0" class="empty-task-blocks">
                暂无任务块，请先在任务块定义配置中创建任务块
              </div>
            </div>
          </div>
          
          <div class="arrangement-panel">
            <div class="panel-header">
              <h4>任务块排列区</h4>
            </div>
            <div 
              class="arrangement-area"
              @dragover="onDragOver"
              @drop="onDrop"
            >
              <div 
                v-for="(taskBlock, index) in arrangedTaskBlocks" 
                :key="taskBlock.instanceId"
                class="arranged-task-block"
                :style="{ backgroundColor: taskBlock.color }"
                draggable="true"
                @dragstart="onArrangedTaskDragStart($event, taskBlock, index)"
                @dragover="onArrangedTaskDragOver($event, index)"
                @drop="onArrangedTaskDrop($event, index)"
              >
                <div class="task-block-content">
                  <div class="task-block-name">{{ taskBlock.name || '未命名任务' }}</div>
                  <div class="task-block-duration">{{ taskBlock.duration }}天</div>
                  <div v-if="taskBlock.requiredDevice" class="task-block-device">设备: {{ taskBlock.requiredDevice }}</div>
                </div>
                <button 
                  class="remove-task-btn" 
                  @click="removeArrangedTask(taskBlock.instanceId)"
                  title="移除"
                >
                  ×
                </button>
              </div>
              
              <div v-if="arrangedTaskBlocks.length === 0" class="empty-arrangement">
                <div class="drop-hint">将任务块拖拽到此处进行排列</div>
              </div>
            </div>
          </div>
        </div>
        
        <div class="modal-footer">
          <button class="cancel-btn" @click="closeTemplateDesign">取消</button>
          <button class="save-btn" @click="saveTemplateDesign">保存模板</button>
        </div>
      </div>
    </div>
    
    <!-- 模板选择弹窗 -->
    <div v-if="templateSelectorVisible" class="modal-overlay" @click="closeTemplateSelector">
      <div class="template-selector-modal" @click.stop>
        <div class="modal-header">
          <h3>为 {{ selectedSatellite.name }} 选择模板</h3>
          <button class="close-btn" @click="closeTemplateSelector">×</button>
        </div>
        
        <div class="template-selector-body">
          <div class="template-selector-content">
            <!-- 左侧：模板列表 -->
             <div class="template-list-section">
               <h4>项目模板</h4>
               <div class="template-list">
                 <div v-for="template in templates" :key="template.id" 
                      class="template-item clickable-template"
                      :class="{ 'selected': selectedTemplate && selectedTemplate.id === template.id && selectedTemplate.type === 'project' }"
                      @click="selectTemplate(template, 'project')">
                   <div class="template-info">
                     <div class="template-name">{{ template.name }}</div>
                   </div>
                 </div>
                 <div v-if="templates.length === 0" class="empty-templates">
                   暂无项目模板，请先在模板配置中创建
                 </div>
               </div>
             </div>
            

          </div>
        </div>
        
        <div class="modal-footer">
          <button class="cancel-btn" @click="closeTemplateSelector">取消</button>
          <button class="copy-btn" @click="showCopySatelliteSelector">复制其他星</button>
          <button class="apply-btn" @click="applySelectedTemplate" :disabled="!selectedTemplate">应用模板</button>
        </div>
      </div>
    </div>
    
    <!-- 复制其他星弹窗 -->
    <div v-if="copySatelliteDialogVisible" class="modal-overlay" @click="closeCopySatelliteDialog">
      <div class="copy-satellite-dialog" @click.stop>
        <div class="dialog-header">
          <h3>复制其他星</h3>
          <button class="close-btn" @click="closeCopySatelliteDialog">×</button>
        </div>
        
        <div class="dialog-body">
          

          
          <div class="source-selection">
            <h4>选择源卫星</h4>
            <div class="satellite-list">
              <div v-for="satellite in availableSourceSatellites" :key="satellite.id" 
                   class="satellite-item"
                   :class="{ 'selected': sourceSatelliteId === satellite.id }"
                   @click="selectSourceSatellite(satellite.id)">
                <div class="satellite-info">
                  <div class="satellite-name">{{ satellite.name }}</div>
                  <div class="satellite-task-count">{{ scheduleItems.filter(item => item.satelliteId === satellite.id).length }} 个任务块</div>
                </div>
              </div>
              
              <div v-if="availableSourceSatellites.length === 0" class="empty-satellites">
                暂无其他卫星可选择
              </div>
            </div>
          </div>
        </div>
        
        <div class="dialog-footer">
          <button class="cancel-btn" @click="closeCopySatelliteDialog">取消</button>
          <button class="confirm-btn" @click="confirmCopySatellite" :disabled="!sourceSatelliteId">确认复制</button>
        </div>
      </div>
    </div>
    
    <!-- 任务块选择器弹窗 -->
    <div v-if="taskBlockSelectorVisible" class="modal-overlay" @click="closeTaskBlockSelector">
      <div class="task-block-selector-modal" @click.stop>
        <div class="modal-header">
          <h3>选择任务块模板</h3>
          <button class="close-btn" @click="closeTaskBlockSelector">×</button>
        </div>
        
        <div class="modal-body">
          <div class="task-block-list">
            <div v-for="taskBlock in availableTaskBlocks" :key="taskBlock.id" 
                 class="task-block-item"
                 :class="{ 'selected': selectedTaskBlock && selectedTaskBlock.id === taskBlock.id }"
                  style="background-color: #87CEEB;"
                 @click="selectTaskBlock(taskBlock)">
              <div class="task-block-content">
                <div class="task-block-name">{{ taskBlock.name }}</div>
                <div class="task-block-info">
                  持续时间: {{ taskBlock.duration }}天
                  <span v-if="taskBlock.requiredDevice"> | 需要设备: {{ taskBlock.requiredDevice }}</span>
                </div>
              </div>
            </div>
            
            <div v-if="availableTaskBlocks.length === 0" class="empty-task-blocks">
              暂无任务块，请先在任务块定义配置中创建
            </div>
          </div>
        </div>
        
        <div class="modal-footer">
          <button class="cancel-btn" @click="closeTaskBlockSelector">取消</button>
          <button 
            class="apply-btn" 
            @click="applySelectedTaskBlock" 
            :disabled="!selectedTaskBlock"
          >
            添加任务块
          </button>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import axios from 'axios'
import config from '@/config'
import DeviceResourceMaintenance from './DeviceResourceMaintenance.vue'
import TaskBlockConfiguration from './TaskBlockConfiguration.vue'
import TemplateConfiguration from './TemplateConfiguration.vue'
import SatelliteProjectNav from './SatelliteProjectNav.vue'
import DeviceQueryGantt from './DeviceQueryGantt.vue'
import { ContextMenuOperations } from '@/utils/contextMenuOperations'
import { TaskBlockEditor } from '@/utils/taskBlockEditor'
import { MouseKeyboardOperations } from '@/utils/mouseKeyboardOperations'
import { ConflictDetection } from '@/utils/ConflictDetection'
import { showTooltip, moveTooltip, hideTooltip } from '@/utils/tooltip.js'
import { TemplateManager } from '@/utils/templateManager'
import { injectSatelliteSchedulingStyles } from '@/utils/satelliteSchedulingStyles.js'

export default {
  name: 'SatelliteScheduling',
  components: {
    DeviceResourceMaintenance,
    TaskBlockConfiguration,
    TemplateConfiguration,
    DeviceQueryGantt,
    SatelliteProjectNav
  },
  data() {
    return {
      // 日期选择和时间相关
      startDate: null, // 初始为null，等待从数据库加载
      dayCount: 30, // 默认30天
      hasScheduleData: false,
      deviceMaintenanceVisible: false,
      taskConfigVisible: false,
      templateConfigVisible: false,
      deviceQueryVisible: false, // 设备查询界面显示状态
      templateSelectorVisible: false,
      selectedSatellite: null,
      selectedTemplate: null,
      // 复制其他星相关
      copySatelliteDialogVisible: false,
      sourceSatelliteId: null,
      copyMode: 'replace', // 只使用替换模式
      isUpdatingScheduleItems: false, // 标志：是否正在批量更新任务块（避免重复保存）
      isSaving: false, // 手动保存状态
      isLoadingScheduleData: false, // 加载排程数据状态
      // 右键菜单相关
      contextMenuVisible: false,
      contextMenuX: 0,
      contextMenuY: 0,
      selectedScheduleItem: null,
      contextMenuSatelliteId: null, // 右键菜单触发时的卫星ID
      contextMenuTimeIndex: null, // 右键菜单触发时的时间索引
      // 任务块选择器相关
      taskBlockSelectorVisible: false,
      selectedTaskBlock: null,
      // 编辑弹窗相关
      editItemVisible: false,
      showDeviceDropdown: false,
      editingItem: {
        id: null,
        title: '',
        satelliteId: null, // 添加卫星ID
        duration: 1,
        requiredDevices: [],
        deviceCapacities: {}, // 设备容量配置 { deviceName: capacity }
        dayConfigs: []
      },
      // 多选相关
      selectedItems: [], // 选中的任务块列表
      isCtrlPressed: false, // 是否按住Ctrl键
      isDraggingMultiple: false, // 是否正在拖拽多个任务块
      isShiftDragging: false, // 是否正在进行Shift+拖拽联动模式
      
      // 框选相关
      isSelecting: false, // 是否正在进行框选
      
      // 撤销功能相关
      historyStack: [], // 历史记录栈
      maxHistorySize: 50, // 最大历史记录数量
      isUndoing: false, // 是否正在执行撤销操作（防止撤销时触发新的历史记录）
      selectionStart: { x: 0, y: 0 }, // 框选起始位置
      selectionCurrent: { x: 0, y: 0 }, // 框选当前位置
      selectionBox: { left: 0, top: 0, width: 0, height: 0 }, // 框选区域
      currentSelectingSatellite: null, // 当前正在框选的卫星ID
      availableDevices: { testCenter: [], assembly: [] },
      templates: [], // 存储模板数据用于模板选择器
      scheduleItems: [],
      // 导航栏的三级结构数据（从SatelliteProjectNav组件获取）
      navigationData: [],
      // 右键菜单操作实例
      contextMenuOps: null,
      // 模板管理工具实例
      templateManager: null,
      // 对齐起始日期时跳过相对位移（防止双重调整）
      isAligningStartDate: false
    }
  },
  computed: {
    allSatellites() {
      const satellites = []
      
      if (!this.navigationData || !Array.isArray(this.navigationData)) {
        return satellites
      }
      
      this.navigationData.forEach((project, projectIndex) => {
        // 检查多种可能的数据结构
        let groupsArray = project.groups || project.satellites || []
        
        if (Array.isArray(groupsArray)) {
          groupsArray.forEach((group, groupIndex) => {
            // 如果group直接是卫星对象
            if (group.id && group.name && !group.satellites) {
              satellites.push(group)
            }
            // 如果group包含satellites数组
            else if (group.satellites && Array.isArray(group.satellites)) {
              satellites.push(...group.satellites)
            }
          })
        }
        
        // 如果项目直接包含卫星数组
        if (project.satellites && Array.isArray(project.satellites)) {
          satellites.push(...project.satellites)
        }
      })
      
      return satellites
    },
    // 计算需要显示的最大天数（确保包含所有任务块）
    requiredDayCount() {
      if (!this.scheduleItems || this.scheduleItems.length === 0) {
        return this.dayCount
      }
      
      // 找到所有任务块相对于startDate的最大范围
      let minDay = 0
      let maxDay = this.dayCount - 1
      
      this.scheduleItems.forEach(item => {
        const startDay = item.startDay || 0
        const duration = parseInt(item.duration) || 1
        const endDay = startDay + duration - 1
        
        minDay = Math.min(minDay, startDay)
        maxDay = Math.max(maxDay, endDay)
      })
      
      // 计算从最早位置到最晚位置需要的总天数
      const totalDaysNeeded = maxDay - minDay + 1
      
      // 确保至少显示用户设定的天数
      return Math.max(this.dayCount, totalDaysNeeded)
    },
    
    // 生成时间槽，每个槽代表1天
    timeSlots() {
      const slots = []
      
      // 始终使用用户设置的起始日期作为基准（本地时区）
      const baseDate = this.parseYMDLocal(this.startDate)
      
      const totalDays = this.requiredDayCount // 使用计算出的天数而不是固定的dayCount
      
      // 每个时间槽代表1天，从startDate开始连续显示
      for (let i = 0; i < totalDays; i++) {
        const slotDate = new Date(baseDate)
        slotDate.setDate(baseDate.getDate() + i)
        
        slots.push({
          startDate: slotDate,
          endDate: slotDate,
          index: i
        })
      }
      
      return slots
    },
    
    // 获取可选择的源卫星列表（排除当前选中的卫星）
    availableSourceSatellites() {
      if (!this.selectedSatellite) {
        return this.allSatellites
      }
      return this.allSatellites.filter(satellite => satellite.id !== this.selectedSatellite.id)
    },
    
    // 获取源卫星的任务块数量
    sourceSatelliteTaskCount() {
      if (!this.sourceSatelliteId) return 0
      return this.scheduleItems.filter(item => item.satelliteId === this.sourceSatelliteId).length
    },
    
    // 获取当前选中卫星的任务块数量
    currentSatelliteTaskCount() {
      if (!this.selectedSatellite) return 0
      return this.scheduleItems.filter(item => item.satelliteId === this.selectedSatellite.id).length
    },
    
    // 判断右键菜单位置是否有任务块
    hasTaskBlockAtContextPosition() {
      // 如果有选中的任务块，直接返回true
      if (this.selectedScheduleItem) {
        return true
      }
      
      // 如果没有选中的任务块但有上下文信息，查找该位置是否有任务块
      if (this.contextMenuSatelliteId !== null && this.contextMenuTimeIndex !== null) {
        return this.scheduleItems.some(item => {
          return item.satelliteId === this.contextMenuSatelliteId && 
                 this.contextMenuTimeIndex >= item.startDay && 
                 this.contextMenuTimeIndex < item.startDay + item.duration
        })
      }
      
      return false
    },
    
    // 获取所有选中任务块的信息
    selectedItemsInfo() {
      if (!this.selectedItems || this.selectedItems.length === 0) {
        return []
      }
      
      return this.selectedItems.map(selectedItem => {
        const scheduleItem = this.scheduleItems.find(item => item.id === selectedItem.id)
        if (scheduleItem) {
          return {
            satelliteId: scheduleItem.satelliteId,
            startDay: scheduleItem.startDay,
            duration: scheduleItem.duration,
            endDay: scheduleItem.startDay + scheduleItem.duration - 1
          }
        }
        return null
      }).filter(item => item !== null)
    }
  },
 
  methods: {
    // ====== 本地时区日期工具函数（统一处理） ======
    toYMDLocal(date) {
      const y = date.getFullYear()
      const m = String(date.getMonth() + 1).padStart(2, '0')
      const d = String(date.getDate()).padStart(2, '0')
      return `${y}-${m}-${d}`
    },
    parseYMDLocal(ymd) {
      if (!ymd) return new Date(NaN)
      const [y, m, d] = (ymd || '').split('-').map(n => parseInt(n, 10))
      return new Date(y, (m || 1) - 1, d || 1) // 本地午夜
    },
    dateDiffInDaysLocal(aYMD, bYMD) {
      if (!aYMD || !bYMD) return 0
      const [ay, am, ad] = aYMD.split('-').map(n => parseInt(n, 10))
      const [by, bm, bd] = bYMD.split('-').map(n => parseInt(n, 10))
      const aUTC = Date.UTC(ay, (am || 1) - 1, ad || 1)
      const bUTC = Date.UTC(by, (bm || 1) - 1, bd || 1)
      const dayMs = 24 * 60 * 60 * 1000
      return Math.floor((aUTC - bUTC) / dayMs)
    },
    // 判断列是否应该高亮
    isColumnHighlighted(satelliteId) {
      return this.selectedItemsInfo.some(item => item.satelliteId === satelliteId)
    },
    
    // 判断行是否应该高亮
    isRowHighlighted(timeIndex) {
      return this.selectedItemsInfo.some(item => 
        timeIndex >= item.startDay && timeIndex <= item.endDay
      )
    },
    
    // 判断单元格是否应该高亮
    isCellHighlighted(satelliteId, timeIndex) {
      return this.selectedItemsInfo.some(item => 
        item.satelliteId === satelliteId && 
        timeIndex >= item.startDay && 
        timeIndex <= item.endDay
      )
    },
    // 悬浮提示：在任务块上显示简要信息
    onScheduleItemMouseEnter(evt, item) {
      const text = this.getItemTooltipText(item)
      showTooltip(text, evt)
    },
    onScheduleItemMouseMove(evt) {
      moveTooltip(evt)
    },
    onScheduleItemMouseLeave() {
      hideTooltip()
    },
    getItemTooltipText(item) {
      // 仅返回任务名称
      return item.title || '未命名任务'
    },
    // 手动保存排程数据
    async manualSave() {
      if (this.isSaving) {
        return // 防止重复保存
      }
      
      this.isSaving = true
      try {
        await this.saveScheduleData() // 手动保存功能
      } catch (error) {
        console.error('保存失败:', error)
        this.$message?.error('保存失败，请重试') || alert('保存失败，请重试')
      } finally {
        this.isSaving = false
      }
    },
    
    // 撤销功能相关方法
    // 保存当前状态到历史记录
    saveToHistory() {
      if (this.isUndoing) {
        return // 撤销操作时不保存历史记录
      }
      
      const currentState = {
        scheduleItems: JSON.parse(JSON.stringify(this.scheduleItems)),
        selectedItems: JSON.parse(JSON.stringify(this.selectedItems)),
        timestamp: Date.now()
      }
      
      this.historyStack.push(currentState)
      
      // 限制历史记录数量
      if (this.historyStack.length > this.maxHistorySize) {
        this.historyStack.shift() // 移除最旧的记录
      }
      
      console.log(`历史记录已保存，当前历史记录数量: ${this.historyStack.length}`)
    },
    
    // 执行撤销操作
    undo() {
      if (this.historyStack.length === 0) {
        console.log('没有可撤销的操作')
        return
      }
      
      this.isUndoing = true
      
      try {
        const previousState = this.historyStack.pop()
        
        // 恢复状态
        this.scheduleItems = JSON.parse(JSON.stringify(previousState.scheduleItems))
        this.selectedItems = JSON.parse(JSON.stringify(previousState.selectedItems))
        
        // 清空选中状态（避免显示问题）
        this.clearSelection()
        
        // 重新检测冲突
        const conflicts = this.detectDeviceConflicts()
        if (conflicts.length > 0) {
          this.markConflictingTasks(conflicts)
        } else {
          this.clearConflictMarks()
        }
        
        console.log(`撤销操作完成，剩余历史记录数量: ${this.historyStack.length}`)
        
        // 显示撤销成功提示
        this.$message?.success('撤销成功') || console.log('撤销成功')
        
      } catch (error) {
        console.error('撤销操作失败:', error)
        this.$message?.error('撤销操作失败') || alert('撤销操作失败')
      } finally {
        this.isUndoing = false
      }
    },
    
    // 加载导航数据（项目、组、卫星）
    async loadNavigationData() {
      try {
        const response = await axios.get(`${config.API_BASE_URL}/api/projects/`)
        
        // 检查响应数据结构并正确提取项目数组
        let projectsData = []
        if (response.data && response.data.success && Array.isArray(response.data.data)) {
          projectsData = response.data.data
        } else if (Array.isArray(response.data)) {
          projectsData = response.data
        } else {
          projectsData = []
        }
        
        this.navigationData = projectsData.map(project => ({
          ...project,
          expanded: false,
          groups: (project.groups || []).map(group => ({
            ...group,
            expanded: false,
            satellites: group.satellites || []
          }))
        }))
        
        // 导航数据加载完成后，重新检查是否应该显示排程表格
        const shouldShowData = this.scheduleItems.length > 0 || this.allSatellites.length > 0
        this.hasScheduleData = shouldShowData
        
        // 强制重新渲染组件以确保表格更新
        this.$forceUpdate()
      } catch (error) {
        // 如果加载失败，使用空数组
        this.navigationData = []
        this.hasScheduleData = this.scheduleItems.length > 0
      }
    },
    
    // 日期和时间相关方法
    incrementDays() {
      this.dayCount += 1
    },
    
    decrementDays() {
      if (this.dayCount > 1) {
        this.dayCount -= 1
      }
    },
    
    // 确保显示天数足够包含所有任务块
    ensureSufficientDays() {
      const maxEndDay = this.scheduleItems.length > 0 ? 
        Math.max(...this.scheduleItems.map(item => {
          const startDay = item.startDay || 0
          const duration = parseInt(item.duration) || 1
          return startDay + duration
        })) : 0
      
      if (maxEndDay > this.dayCount) {
        this.dayCount = maxEndDay
      }
    },
    
    formatTimeSlot(timeSlot) {
      const formatDate = (date) => {
        const month = (date.getMonth() + 1).toString().padStart(2, '0')
        const day = date.getDate().toString().padStart(2, '0')
        return `${month}/${day}`
      }
      
      return formatDate(timeSlot.startDate)
    },
    
    async startScheduling() {
      // 加载导航数据以确保卫星列表显示
      await this.loadNavigationData()
      // 确保有导航数据或排程数据时显示表格
      this.hasScheduleData = this.scheduleItems.length > 0 || this.allSatellites.length > 0
    },
    addScheduleItem(satelliteId, timeIndex) {
      // 设置标志防止watcher重复保存
      this.isUpdatingScheduleItems = true
      
      // 计算实际的起始日期
      const actualStartDate = this.parseYMDLocal(this.startDate)
      actualStartDate.setDate(actualStartDate.getDate() + timeIndex)
      
      // 创建新的任务块
      const newItem = {
        id: Date.now(), // 使用时间戳作为临时ID
        title: '新任务',
        satelliteId: satelliteId,
        startDay: timeIndex,
        duration: 1, // 默认持续1天
        color: '#6c757d',
        type: 'task',
        requiredDevices: [], // 初始化设备列表
        deviceCapacities: {}, // 初始化设备容量配置
        dayConfigs: [], // 初始化日配置
        actualStartDate: this.toYMDLocal(actualStartDate) // 记录实际起始日期（本地）
      }
      this.scheduleItems.push(newItem)
      
      // 重置标志
      this.isUpdatingScheduleItems = false
      
      // 确保显示天数足够包含新添加的任务块
      this.ensureSufficientDays()
    },
    getScheduleItems(satelliteId, timeIndex) {
      // 返回指定卫星在指定时间槽的排程项目
      return this.scheduleItems.filter(item => {
        return item.satelliteId === satelliteId && 
               timeIndex >= item.startDay && 
               timeIndex < item.startDay + item.duration
      })
    },
    getScheduleItemsAtStart(satelliteId, timeIndex) {
      // 返回在指定时间槽开始显示的任务块
      // 对于startDay为负数的任务块，在timeIndex=0时显示
      const items = this.scheduleItems.filter(item => {
        if (item.satelliteId !== satelliteId) return false
        
        // 如果任务块的startDay为负数，在timeIndex=0时显示
        if (item.startDay < 0) {
          return timeIndex === 0
        }
        
        // 正常情况下，在startDay对应的timeIndex显示
        return item.startDay === timeIndex
      })
      
      return items
    },
    
    // 计算任务块在当前时间范围内的可见持续时间
    getVisibleDuration(item, timeIndex) {
      const totalDays = this.requiredDayCount
      
      // 如果任务块的startDay为负数，计算从timeIndex=0开始的可见部分
      if (item.startDay < 0) {
        const remainingDuration = item.duration + item.startDay // 减去负数部分
        return Math.min(remainingDuration, totalDays)
      }
      
      // 正常情况下，返回完整持续时间，但不超过剩余的时间槽数量
      const remainingSlots = totalDays - timeIndex
      return Math.min(item.duration, remainingSlots)
    },
    
    showDeviceMaintenance() {
      this.deviceMaintenanceVisible = true
    },
    handleDeviceSave(deviceData) {
      // 更新可用设备列表
      this.availableDevices = {
        testCenter: deviceData.testCenter || [],
        assembly: deviceData.assembly || []
      }
    },
    
    async showTemplateSelector(satellite) {
      return this.templateManager.showTemplateSelector(satellite)
    },
    
    closeTemplateSelector() {
      this.templateManager.closeTemplateSelector()
    },
    
    // 复制其他星功能相关方法
    showCopySatelliteSelector() {
      this.templateManager.showCopySatelliteSelector()
    },
    
    closeCopySatelliteDialog() {
      this.templateManager.closeCopySatelliteDialog()
    },
    
    selectSourceSatellite(satelliteId) {
      this.templateManager.selectSourceSatellite(satelliteId)
    },
    
    async confirmCopySatellite() {
      return this.templateManager.confirmCopySatellite()
    },
     
     // 验证并修复任务块重叠问题
    validateAndFixOverlaps(satelliteId) {
      // 获取该卫星的所有任务块，按开始时间排序
      const satelliteTasks = this.scheduleItems
        .filter(item => item.satelliteId === satelliteId)
        .sort((a, b) => a.startDay - b.startDay)
      
      if (satelliteTasks.length <= 1) {
        return
      }
      
      // 检查并修复重叠
      let hasOverlap = false
      for (let i = 0; i < satelliteTasks.length - 1; i++) {
        const currentTask = satelliteTasks[i]
        const nextTask = satelliteTasks[i + 1]
        
        const currentEnd = currentTask.startDay + currentTask.duration
        
        if (currentEnd > nextTask.startDay) {
          hasOverlap = true
          
          // 修复重叠：将后续任务块移动到当前任务块结束后
          const newStartDay = currentEnd
          const taskIndex = this.scheduleItems.findIndex(item => item.id === nextTask.id)
          if (taskIndex > -1) {
            this.scheduleItems[taskIndex] = {
              ...this.scheduleItems[taskIndex],
              startDay: newStartDay
            }
            
            // 更新本地数组中的任务块信息
            satelliteTasks[i + 1].startDay = newStartDay
          }
        }
      }
    },
    
    selectTemplate(template, type) {
      this.templateManager.selectTemplate(template, type)
    },
    
    applySelectedTemplate() {
      this.templateManager.applySelectedTemplate()
    },
    
    applyTaskBlockToSatellite(taskBlock, satellite) {
       // 这里实现将任务块应用到指定卫星的逻辑
       
       // 找到该卫星第一个可用的时间槽
       const findAvailableStartDay = () => {
         const satelliteItems = this.scheduleItems.filter(item => item.satelliteId === satellite.id)
         
         // 如果没有任务块，从第0天开始
         if (satelliteItems.length === 0) {
           return 0
         }
         
         // 找到最晚结束的任务块
         const maxEndDay = Math.max(...satelliteItems.map(item => item.startDay + item.duration))
         return maxEndDay
       }
       
       // 创建调度项目
       const duration = parseInt(taskBlock.duration) || 1
       
       // 提取任务块的设备信息
       const extractedDevices = this.extractRequiredDevicesFromTemplate(taskBlock)
       
       // 自动激活所有天数的设备配置
       let appliedDayConfigs = []
       // 优先使用模板中保存的按天设备配置
       if (taskBlock.dayConfigs && Array.isArray(taskBlock.dayConfigs) && taskBlock.dayConfigs.length > 0) {
         appliedDayConfigs = [...taskBlock.dayConfigs]
         while (appliedDayConfigs.length < duration) {
           appliedDayConfigs.push([])
         }
       } else if (extractedDevices.length > 0) {
         // 其次使用总体所需设备，重复到每一天
         for (let i = 0; i < duration; i++) {
           appliedDayConfigs.push([...extractedDevices])
         }
       } else {
         // 如果没有任何设备配置，创建空的配置
         for (let i = 0; i < duration; i++) {
           appliedDayConfigs.push([])
         }
       }

        const startDay = findAvailableStartDay()
        
        // 计算实际的起始日期
        const actualStartDate = this.parseYMDLocal(this.startDate)
        actualStartDate.setDate(actualStartDate.getDate() + startDay)
        
        const scheduleItem = {
          id: Date.now() + Math.random(),
          satelliteId: satellite.id,
         title: taskBlock.name,
         type: 'task',
         startDay: startDay, // 智能设置开始时间
         duration: duration,
         color: taskBlock.color || '#3498db',
         requiredDevice: taskBlock.requiredDevice || '',
         requiredDevices: this.extractRequiredDevicesFromTemplate(taskBlock),
          dayConfigs: appliedDayConfigs,
         description: `从任务块模板导入: ${taskBlock.name}`,
          // 添加设备容量配置
          deviceCapacities: this.createDeviceCapacitiesFromTemplate(taskBlock),
          actualStartDate: this.toYMDLocal(actualStartDate) // 记录实际起始日期（本地）
       }
       
       // 设置标志防止watcher重复保存
       this.isUpdatingScheduleItems = true
       
       // 添加到调度数据中
       this.scheduleItems.push(scheduleItem)
       
       this.hasScheduleData = true
       
       // 重置标志（已移除自动保存）
       this.isUpdatingScheduleItems = false
       
       // 自动进行设备冲突检测
       const conflicts = this.detectDeviceConflicts()
       if (conflicts.length > 0) {
         this.markConflictingTasks(conflicts)
       } else {
         this.clearConflictMarks()
       }
       
       // 触发数据更新
       this.$forceUpdate()
       
       // 确保显示天数足够包含新添加的任务块
       this.ensureSufficientDays()
     },
    

    
    async loadTaskBlocks() {
      // 从后端API加载任务块数据
      try {
        const response = await axios.get(`${config.API_BASE_URL}/api/templates/available_taskblocks/`)
        
        // 检查响应数据结构并正确提取任务块数组
        const taskBlocksData = response.data.success && Array.isArray(response.data.data) 
          ? response.data.data 
          : (Array.isArray(response.data) ? response.data : [])
        
        this.availableTaskBlocks = taskBlocksData.map(task => {
          // 解析daily_device_config字段
          let deviceConfig = {}
          if (task.daily_device_config) {
            try {
              deviceConfig = JSON.parse(task.daily_device_config)
            } catch (e) {
              deviceConfig = {}
            }
          }
          
          return {
            id: task.id,
            name: task.name,
            duration: task.duration,
            color: deviceConfig.color || task.color || '#87CEEB',
            requiredDevices: deviceConfig.devices || [],
            requiredDevice: task.requiredDevice || '',
            deviceCapacities: deviceConfig.deviceCapacities || {},
            dayConfigs: deviceConfig.dayConfigs || [],
            status: task.status
          }
        })
      } catch (error) {
        this.availableTaskBlocks = []
      }
    },
    
    showTaskConfig() {
      this.taskConfigVisible = true
    },
    
    handleTaskSave(taskData) {
      // 这里可以处理任务块数据的保存逻辑
    },
    
    showTemplateConfig() {
      this.templateConfigVisible = true
    },

    showDeviceQuery() {
      this.deviceQueryVisible = true
    },

    handleTemplatesUpdated(templates) {
      // 当模板组件更新模板时，更新本地的模板数据用于模板选择器
      this.templates = templates
    },

    async loadAvailableDevices() {
      // 从API加载设备数据
      try {
        const response = await axios.get('/api/equipment/')
        
        if (response.data && response.data.success && Array.isArray(response.data.data)) {
          // 按组织分类设备数据，并转换为组件期望的格式
          const testCenter = response.data.data
            .filter(device => device.organization === '试验中心')
            .map(device => ({
              id: device.id,
              name: device.device_name,
              quantity: device.device_quantity,
              capacity: device.device_capacity,
              organization: device.organization
            }))
          
          const assembly = response.data.data
            .filter(device => device.organization === '总装&机构展开')
            .map(device => ({
              id: device.id,
              name: device.device_name,
              quantity: device.device_quantity,
              capacity: device.device_capacity,
              organization: device.organization
            }))
          
          this.availableDevices = {
            testCenter: testCenter,
            assembly: assembly
          }
        } else {
          this.availableDevices = {
            testCenter: [],
            assembly: []
          }
        }
      } catch (error) {
        this.availableDevices = {
          testCenter: [],
          assembly: []
        }
      }
    },
    
    // 加载排程数据
    async loadScheduleData() {
      // 防止重复调用
      if (this.isLoadingScheduleData) {
        console.log('loadScheduleData already in progress, skipping...')
        return
      }
      
      this.isLoadingScheduleData = true
      
      try {
        // 先清空现有数据，防止重复
        this.scheduleItems = []
        
        const response = await axios.get('/api/scheduling/satellite-schedules/')
        
        // 处理分页响应格式 {count: 4, results: Array(4)}
        const scheduleData = response.data.results || response.data
        
        if (scheduleData && scheduleData.length > 0) {
          // 将数据库中的排程数据转换为前端格式
          const existingIds = new Set() // 用于去重的ID集合
          let loadedStartDate = null // 后端返回的基准开始日期
          
          // 安全添加任务项的辅助函数
          const addUniqueItems = (items) => {
            if (Array.isArray(items)) {
              items.forEach(item => {
                if (item && item.id && !existingIds.has(item.id)) {
                  existingIds.add(item.id)
                  this.scheduleItems.push(item)
                }
              })
            }
          }
          
          scheduleData.forEach(schedule => {
            if (schedule.schedule_info) {
              try {
                // schedule_info现在是对象，不需要JSON.parse
                const scheduleInfo = typeof schedule.schedule_info === 'string' 
                  ? JSON.parse(schedule.schedule_info) 
                  : schedule.schedule_info
                
                // 如果后端包含基准开始日期，记录一次（取第一条）
                if (!loadedStartDate && scheduleInfo && scheduleInfo.startDate) {
                  loadedStartDate = scheduleInfo.startDate
                }

                // 检查多种可能的数据格式
                if (scheduleInfo.scheduleItems && Array.isArray(scheduleInfo.scheduleItems)) {
                  // 格式1: {scheduleItems: [...]} 
                  addUniqueItems(scheduleInfo.scheduleItems)
                } else if (Array.isArray(scheduleInfo)) {
                  // 格式2: 直接是数组
                  addUniqueItems(scheduleInfo)
                } else if (scheduleInfo.lastUpdated && scheduleInfo.scheduleItems) {
                  // 格式3: {lastUpdated: "...", scheduleItems: [...]} - 用户数据库中的格式
                  addUniqueItems(scheduleInfo.scheduleItems)
                } else {
                  // 未识别的数据格式，跳过
                }
              } catch (parseError) {
                console.warn('解析排程数据失败:', parseError)
                // 解析失败，跳过这条记录
              }
            }
          })
          
          // 只要有排程数据或导航数据就设置hasScheduleData为true
          const shouldShowData = this.scheduleItems.length > 0 || this.allSatellites.length > 0
          this.hasScheduleData = shouldShowData
          
          // 加载完成后进行设备冲突检测
          if (this.scheduleItems.length > 0) {
            this.$nextTick(() => {
              const conflicts = this.detectDeviceConflicts()
              if (conflicts.length > 0) {
                this.markConflictingTasks(conflicts)
              }
              
              // 确保显示天数足够包含所有加载的任务块
              this.ensureSufficientDays()
            })
          }
          
          // 如果后端提供了startDate，则设置为前端的基准开始日期（避免相对位移）
          if (loadedStartDate) {
            this.isAligningStartDate = true
            this.startDate = loadedStartDate
            this.isAligningStartDate = false
          } else {
            // 如果数据库中没有startDate，使用今天作为默认值
            const today = new Date()
            const y = today.getFullYear()
            const m = String(today.getMonth() + 1).padStart(2, '0')
            const dd = String(today.getDate()).padStart(2, '0')
            this.startDate = `${y}-${m}-${dd}`
          }
        } else {
          this.scheduleItems = []
          this.hasScheduleData = this.allSatellites.length > 0 // 即使没有排程数据，有卫星数据也显示表格
          
          // 如果没有排程数据，设置默认的startDate为今天
          if (!this.startDate) {
            const today = new Date()
            const y = today.getFullYear()
            const m = String(today.getMonth() + 1).padStart(2, '0')
            const dd = String(today.getDate()).padStart(2, '0')
            this.startDate = `${y}-${m}-${dd}`
          }
        }
      } catch (error) {
        console.error('加载排程数据失败:', error)
        // 如果API调用失败，初始化为空数据，但如果有导航数据还是显示表格
        this.scheduleItems = []
        this.hasScheduleData = this.allSatellites.length > 0
        
        // 如果加载失败且没有startDate，设置默认值
        if (!this.startDate) {
          const today = new Date()
          const y = today.getFullYear()
          const m = String(today.getMonth() + 1).padStart(2, '0')
          const dd = String(today.getDate()).padStart(2, '0')
          this.startDate = `${y}-${m}-${dd}`
        }
      } finally {
        // 无论成功还是失败，都要重置加载状态
        this.isLoadingScheduleData = false
      }
    },
    
    // 保存排程数据到数据库
    async saveScheduleData() {
      try {
        const scheduleData = {
          scheduleItems: this.scheduleItems,
          hasScheduleData: this.hasScheduleData,
          lastUpdated: new Date().toISOString()
        }
        
        // 按卫星分组排程数据
        const satelliteSchedules = {}
        this.scheduleItems.forEach(item => {
          const satelliteId = item.satelliteId || 'default'
          if (!satelliteSchedules[satelliteId]) {
            satelliteSchedules[satelliteId] = []
          }
          satelliteSchedules[satelliteId].push(item)
        })
        
        // 构建符合后端API格式的数据
        const schedules = Object.keys(satelliteSchedules).map(satelliteId => ({
          satellite: satelliteId,
          schedule_info: {
            scheduleItems: satelliteSchedules[satelliteId],
            hasScheduleData: this.hasScheduleData,
            lastUpdated: new Date().toISOString()
          }
        }))
        
        // 调用后端API保存排程数据
        const response = await axios.post('/api/scheduling/satellite-schedules/bulk_create_or_update/', {
          schedules: schedules,
          startDate: this.startDate  // 传递基准开始日期
        })
        
        if (response.data) {
          // 保存成功
        }
      } catch (error) {
        // 可以选择显示用户友好的错误提示
        // alert('保存排程数据失败，请检查网络连接')
      }
    },
    
    // 更新起始日期为所有任务块的最早时间
    updateStartDateFromTasks() {
      // 设置对齐标志，避免在更新startDate时触发相对位移
      this.isAligningStartDate = true
      if (!this.scheduleItems || this.scheduleItems.length === 0) {
        // 如果没有任务块，保持当前日期
        this.isAligningStartDate = false
        return
      }
      
      // 找到所有任务块中最早的实际起始日期
      let earliestDate = null
      
      this.scheduleItems.forEach(item => {
        if (item.actualStartDate) {
          const itemDate = new Date(item.actualStartDate)
          if (!earliestDate || itemDate < earliestDate) {
            earliestDate = itemDate
          }
        }
      })
      
      // 如果找到了最早的实际起始日期，更新startDate（本地）
      if (earliestDate) {
        this.startDate = this.toYMDLocal(earliestDate)

        // 重新计算所有任务块的startDay（相对于新的startDate）
        this.scheduleItems.forEach(item => {
          if (item.actualStartDate) {
            const itemDate = this.parseYMDLocal(item.actualStartDate)
            const dayDiff = this.dateDiffInDaysLocal(this.toYMDLocal(itemDate), this.toYMDLocal(earliestDate))
            item.startDay = dayDiff
          }
        })
      }
      // 重置对齐标志
      this.isAligningStartDate = false
    },
    
    // 任务块拖拽开始
    onScheduleItemDragStart(event, item, index, satelliteId) {
      if (this.mouseKeyboardOps) {
        this.mouseKeyboardOps.onScheduleItemDragStart(event, item, index, satelliteId)
      }
    },
    
    // 任务块拖拽结束（处理拖拽取消的情况）
    onScheduleItemDragEnd(event, item) {
      if (this.mouseKeyboardOps) {
        this.mouseKeyboardOps.onScheduleItemDragEnd(event, item)
      }
    },
    
    // 任务块拖拽悬停
    onScheduleItemDragOver(event, targetIndex) {
      event.preventDefault()
      event.dataTransfer.dropEffect = 'move'
    },
    
    // 任务块拖拽放置
    onScheduleItemDrop(event, targetIndex, targetSatelliteId) {
      event.preventDefault()
      event.stopPropagation()
      
      // 在移动前保存历史记录
      this.saveToHistory()
      
      // 设置标志防止watcher重复保存
      this.isUpdatingScheduleItems = true
      
      const dragData = JSON.parse(event.dataTransfer.getData('text/plain'))
      
      if (dragData.isMultipleItemDrag) {
        // 多选任务块拖拽
        const targetSatelliteItems = this.scheduleItems.filter(item => item.satelliteId === targetSatelliteId)
        
        // 从原位置移除所有选中的任务块
        this.selectedItems.forEach(selectedItem => {
          const index = this.scheduleItems.findIndex(item => item.id === selectedItem.id)
          if (index > -1) {
            this.scheduleItems.splice(index, 1)
          }
        })
        
        // 重新计算目标位置在全局数组中的索引
        const updatedTargetSatelliteItems = this.scheduleItems.filter(item => item.satelliteId === targetSatelliteId)
        let globalTargetIndex
        
        if (targetIndex >= updatedTargetSatelliteItems.length) {
          // 插入到最后
          globalTargetIndex = this.scheduleItems.length
        } else {
          // 找到目标位置的全局索引
          const targetItem = updatedTargetSatelliteItems[targetIndex]
          globalTargetIndex = this.scheduleItems.findIndex(item => item.id === targetItem.id)
        }
        
        // 在目标位置插入所有选中的任务块
        this.selectedItems.forEach((item, index) => {
          // 更新任务块的卫星ID
          const updatedItem = { ...item, satelliteId: targetSatelliteId }
          this.scheduleItems.splice(globalTargetIndex + index, 0, updatedItem)
        })
        
        // 清空选中状态
        this.clearSelection()
        
        // 强制Vue重新渲染，然后恢复任务块的可见性
        this.$nextTick(() => {
          this.restoreTaskVisibility()
        })
        
      } else if (dragData.isScheduleItemReorder && dragData.sourceSatelliteId === targetSatelliteId) {
        // 单个任务块重排序操作

        const sourceIndex = dragData.sourceIndex
        
        // 获取要移动的任务块
        const item = dragData.item

        
        if (!item) {
          return
        }
        
        // 计算考虑拖拽偏移量的实际放置位置
        const dragOffsetDays = dragData.dragOffsetDays || 0
        let calculatedStartDay = targetTimeIndex - dragOffsetDays
        
        // 智能对齐：如果计算出的起始位置在日期内部（小数部分），自动对齐到该日期的开始
        const actualStartDay = Math.max(0, Math.floor(calculatedStartDay))

        
        // 自动排列后续可能冲突的任务块
        this.autoArrangeSubsequentTasks(
          targetSatelliteId, 
          actualStartDay, 
          item.duration, 
          [item.id]
        )
        
        // 更新任务块的开始时间和实际起始日期
        const itemIndex = this.scheduleItems.findIndex(scheduleItem => scheduleItem.id === item.id)
        if (itemIndex > -1) {
          // 计算新的实际起始日期
          const newActualStartDate = this.parseYMDLocal(this.startDate)
          newActualStartDate.setDate(newActualStartDate.getDate() + actualStartDay)
          
          this.scheduleItems[itemIndex] = {
            ...this.scheduleItems[itemIndex],
            startDay: actualStartDay,
            actualStartDate: this.toYMDLocal(newActualStartDate)
          }
        }
        
        // 验证没有重叠（开发调试）
        this.validateNoOverlap(targetSatelliteId)
        
        // 进行全局设备冲突检测
        const conflicts = this.detectDeviceConflicts()
        if (conflicts.length > 0) {
          this.markConflictingTasks(conflicts)
        } else {
          this.clearConflictMarks()
        }
        
        // 清空选中状态（保持与多选拖拽一致的行为）
        this.clearSelection()
        
      } else if ((dragData.item || dragData.isScheduleItemReorder) && dragData.sourceSatelliteId === targetSatelliteId) {
        // 单个任务块拖拽到新时间位置

        const item = dragData.item

        
        if (!item) {
          return
        }
        
        const dragOffsetDays = dragData.dragOffsetDays || 0
        const actualStartDay = Math.max(0, targetTimeIndex - dragOffsetDays)
        
        // 自动排列后续可能冲突的任务块
        this.autoArrangeSubsequentTasks(
          targetSatelliteId, 
          actualStartDay, 
          item.duration, 
          [item.id]
        )
        
        // 更新任务块的开始时间和实际起始日期
        const itemIndex = this.scheduleItems.findIndex(scheduleItem => scheduleItem.id === item.id)
        if (itemIndex > -1) {
          // 计算新的实际起始日期
          const newActualStartDate = new Date(this.startDate)
          newActualStartDate.setDate(newActualStartDate.getDate() + actualStartDay)
          
          this.scheduleItems[itemIndex] = {
            ...this.scheduleItems[itemIndex],
            startDay: actualStartDay,
            actualStartDate: this.toYMDLocal(newActualStartDate)
          }
        } else {
          console.error('未找到要更新的任务块')
        }
        
        // 验证没有重叠（开发调试）
        this.validateNoOverlap(targetSatelliteId)
        
        // 进行全局设备冲突检测
        const conflicts = this.detectDeviceConflicts()
        if (conflicts.length > 0) {
          this.markConflictingTasks(conflicts)
        } else {
          this.clearConflictMarks()
        }
        
        // 清空选中状态（保持与多选拖拽一致的行为）
        this.clearSelection()
        
      } else {

      }
      
      // 重置拖拽状态
      this.isDraggingMultiple = false
      
      // 重置标志
      this.isUpdatingScheduleItems = false
      
      // 强制Vue重新渲染，然后恢复任务块的可见性
      this.$nextTick(() => {
        this.restoreTaskVisibility()
      })
    },
    
    // 单元格拖拽悬停
    onCellDragOver(event, satelliteId, timeIndex) {
      this.mouseKeyboardOps.onCellDragOver(event, satelliteId, timeIndex)
    },
    
    // 单元格拖拽进入
    onCellDragEnter(event, satelliteId, timeIndex) {
      this.mouseKeyboardOps.onCellDragEnter(event, satelliteId, timeIndex)
    },
    
    // 单元格拖拽离开
    onCellDragLeave(event, satelliteId, timeIndex) {
      this.mouseKeyboardOps.onCellDragLeave(event, satelliteId, timeIndex)
    },
    
    // 单元格拖拽放置
    onCellDrop(event, targetSatelliteId, targetTimeIndex) {
      this.mouseKeyboardOps.onCellDrop(event, targetSatelliteId, targetTimeIndex)
    },
    
    // 恢复任务块的可见性
    restoreTaskVisibility() {
      this.mouseKeyboardOps.restoreTaskVisibility()
    },
    
    // 显示右键菜单
    showContextMenu(event, item) {
      if (this.contextMenuOps) {
        this.contextMenuOps.showContextMenu(event, item)
      }
    },
    
    // 隐藏右键菜单
    hideContextMenu() {
      if (this.contextMenuOps) {
        this.contextMenuOps.hideContextMenu()
      }
    },
    
    // 显示单元格右键菜单
    showCellContextMenu(event, satelliteId, timeIndex) {
      if (this.contextMenuOps) {
        this.contextMenuOps.showCellContextMenu(event, satelliteId, timeIndex)
      }
    },
    
    // 显示任务块选择器
    async showTaskBlockSelector() {
      if (this.contextMenuOps) {
        await this.contextMenuOps.showTaskBlockSelector()
      }
    },
    
    // 关闭任务块选择器
    closeTaskBlockSelector() {
      this.taskBlockSelectorVisible = false
      this.selectedTaskBlock = null
      
      // 清除上下文信息
      this.contextMenuSatelliteId = null
      this.contextMenuTimeIndex = null
      this.selectedScheduleItem = null
    },
    
    // 选择任务块
    selectTaskBlock(taskBlock) {
      this.selectedTaskBlock = taskBlock
    },
    
    // 应用选择的任务块
    async applySelectedTaskBlock() {
      if (this.contextMenuOps) {
        await this.contextMenuOps.applySelectedTaskBlock()
      }
    },
    
    // 在指定位置插入任务块，推移后面的任务块
    async insertTaskBlockAt(satelliteId, insertPosition, duration) {
      const satelliteItems = this.scheduleItems.filter(item => item.satelliteId === satelliteId)
      
      // 找到所有在插入位置之后的任务块
      const itemsToMove = satelliteItems.filter(item => item.startDay >= insertPosition)
      
      if (itemsToMove.length === 0) {
        // 如果后面没有任务块，直接在插入位置添加
        return insertPosition
      }
      
      // 按开始时间排序
      itemsToMove.sort((a, b) => a.startDay - b.startDay)
      
      // 检查是否需要推移任务块
      const newTaskEndDay = insertPosition + duration
      let needToMove = false
      
      for (const item of itemsToMove) {
        if (item.startDay < newTaskEndDay) {
          needToMove = true
          break
        }
      }
      
      if (needToMove) {
         // 需要推移任务块
         let currentPosition = newTaskEndDay
         
         for (const item of itemsToMove) {
           if (item.startDay < currentPosition) {
             // 推移这个任务块
             item.startDay = currentPosition
           }
           currentPosition = Math.max(currentPosition, item.startDay + item.duration)
         }
         
         // 保存数据
         // await this.saveScheduleData() // 自动保存已注释
       }
      
      return insertPosition
    },
    
    // 找到下一个可用的开始时间
    findNextAvailableStartDay(satelliteId, preferredStartDay, duration) {
      const satelliteItems = this.scheduleItems.filter(item => item.satelliteId === satelliteId)
      
      // 从首选开始日期开始检查
      let currentStartDay = preferredStartDay
      
      // 检查是否有冲突
      while (this.hasConflict(satelliteId, currentStartDay, duration)) {
        currentStartDay++
        // 防止无限循环，最多向后查找100天
        if (currentStartDay - preferredStartDay > 100) {
          break
        }
      }
      
      return currentStartDay
    },
    
    // 检查指定时间段是否有冲突
    hasConflict(satelliteId, startDay, duration) {
      const satelliteItems = this.scheduleItems.filter(item => item.satelliteId === satelliteId)
      const endDay = startDay + duration
      
      for (const item of satelliteItems) {
        const itemEndDay = item.startDay + item.duration
        
        // 检查时间重叠：新任务的开始时间小于现有任务的结束时间，且新任务的结束时间大于现有任务的开始时间
        if (startDay < itemEndDay && endDay > item.startDay) {
          return true
        }
      }
      
      return false
    },
    
    // 编辑任务块
    editScheduleItem() {
      if (this.taskBlockEditor) {
        this.taskBlockEditor.editScheduleItem()
      }
    },
    
    // 关闭编辑弹窗
    closeEditItem() {
      if (this.taskBlockEditor) {
        this.taskBlockEditor.closeEditItem()
      }
    },
    
    // 保存编辑的任务块
    async saveEditItem() {
      if (this.taskBlockEditor) {
        await this.taskBlockEditor.saveEditItem()
      }
    },
    
    // 删除任务块
    async deleteScheduleItem() {
      if (this.contextMenuOps) {
        await this.contextMenuOps.deleteScheduleItem()
      }
    },
    
    // 删除卫星排程记录
    async deleteSatelliteScheduleRecord(satelliteId) {
      try {
        // 调用后端API删除整条卫星排程记录
        const response = await axios.delete(`/api/scheduling/satellite-schedules/delete_by_satellite/${satelliteId}/`)
        
        if (response.data && !response.data.success) {
          alert(`删除卫星排程记录失败`)
        }
      } catch (error) {
        alert(`删除卫星排程记录失败: ${error.response ? error.response.data.error || error.response.statusText : error.message}`)
      }
    },
    
    // 设备选择相关方法
    toggleDeviceDropdown() {
      this.showDeviceDropdown = !this.showDeviceDropdown
    },
    
    async toggleDeviceSelection(deviceName) {
      const index = this.editingItem.requiredDevices.indexOf(deviceName)
      if (index > -1) {
        // 如果已选择，则移除
        this.editingItem.requiredDevices.splice(index, 1)
        // 移除设备时也删除其容量配置
        delete this.editingItem.deviceCapacities[deviceName]
      } else {
        // 如果未选择，则添加
        this.editingItem.requiredDevices.push(deviceName)
        // 添加设备时，优先从同一卫星的相同任务块名称中获取设备容量
        const sameTaskCapacity = this.getDeviceCapacityFromSameTaskName(this.editingItem.satelliteId, this.editingItem.title, deviceName)
        if (sameTaskCapacity !== null) {
          this.editingItem.deviceCapacities[deviceName] = sameTaskCapacity
        } else {
          // 如果同一卫星中没有相同任务块，使用默认容量
          this.editingItem.deviceCapacities[deviceName] = this.getDefaultDeviceCapacity(deviceName)
        }
      }
      
      // 更新实际的scheduleItems中的数据并自动保存
      const itemIndex = this.scheduleItems.findIndex(item => item.id === this.editingItem.id);
      if (itemIndex !== -1) {
        this.scheduleItems[itemIndex].requiredDevices = [...this.editingItem.requiredDevices];
        this.scheduleItems[itemIndex].deviceCapacities = {...this.editingItem.deviceCapacities};
        // await this.saveScheduleData();
      }
    },
    
    async removeDevice(deviceName) {
      const index = this.editingItem.requiredDevices.indexOf(deviceName)
      if (index > -1) {
        this.editingItem.requiredDevices.splice(index, 1)
        // 移除设备时也删除其容量配置
        delete this.editingItem.deviceCapacities[deviceName]
        
        // 更新实际的scheduleItems中的数据并自动保存
        const itemIndex = this.scheduleItems.findIndex(item => item.id === this.editingItem.id);
        if (itemIndex !== -1) {
          this.scheduleItems[itemIndex].requiredDevices = [...this.editingItem.requiredDevices];
          this.scheduleItems[itemIndex].deviceCapacities = {...this.editingItem.deviceCapacities};
          // await this.saveScheduleData();
        }
      }
    },

    // 获取设备的默认容量
    getDefaultDeviceCapacity(deviceName) {
      // 从设备资源维护中查找设备的默认容量（总设备容量）
      const testCenterDevice = this.availableDevices.testCenter.find(device => device.name === deviceName)
      if (testCenterDevice) {
        return testCenterDevice.capacity || 1
      }
      
      const assemblyDevice = this.availableDevices.assembly.find(device => device.name === deviceName)
      if (assemblyDevice) {
        return assemblyDevice.capacity || 1
      }
      
      return 1 // 默认容量为1
    },

    // 从模板任务块提取所需设备列表
    extractRequiredDevicesFromTemplate(taskBlock) {
      let requiredDevices = []
      
      // 优先使用 requiredDevices 数组
      if (taskBlock.requiredDevices && Array.isArray(taskBlock.requiredDevices) && taskBlock.requiredDevices.length > 0) {
        requiredDevices = [...taskBlock.requiredDevices]
      } 
      // 其次使用 requiredDevice 单个设备（兼容性）
      else if (taskBlock.requiredDevice && taskBlock.requiredDevice.trim() !== '') {
        requiredDevices = [taskBlock.requiredDevice]
      }
      // 最后尝试从 dayConfigs 中提取设备信息
      else if (taskBlock.dayConfigs && Array.isArray(taskBlock.dayConfigs)) {
        const devicesFromDayConfigs = new Set()
        taskBlock.dayConfigs.forEach(dayConfig => {
          if (Array.isArray(dayConfig)) {
            dayConfig.forEach(device => {
              if (device && device.trim() !== '') {
                devicesFromDayConfigs.add(device)
              }
            })
          }
        })
        requiredDevices = Array.from(devicesFromDayConfigs)
      }
      
      return requiredDevices
    },

    // 从模板任务块创建设备容量配置
    createDeviceCapacitiesFromTemplate(taskBlock) {
      const deviceCapacities = {}
      
      // 如果模板任务块已经有设备容量配置，使用它
      if (taskBlock.deviceCapacities && typeof taskBlock.deviceCapacities === 'object') {
        Object.assign(deviceCapacities, taskBlock.deviceCapacities)
      }
      
      // 确保所有需要的设备都有容量配置
      const allRequiredDevices = []
      if (taskBlock.requiredDevices && Array.isArray(taskBlock.requiredDevices)) {
        allRequiredDevices.push(...taskBlock.requiredDevices)
      } else if (taskBlock.requiredDevice) {
        allRequiredDevices.push(taskBlock.requiredDevice)
      }
      
      // 为没有容量配置的设备设置容量（优先从同一卫星相同任务块获取）
      allRequiredDevices.forEach(deviceName => {
        if (!deviceCapacities[deviceName]) {
          // 先尝试从同一卫星的相同任务块名称中获取设备容量
          const sameTaskCapacity = this.getDeviceCapacityFromSameTaskName(taskBlock.satelliteId, taskBlock.name, deviceName)
          if (sameTaskCapacity !== null) {
            deviceCapacities[deviceName] = sameTaskCapacity
          } else {
            // 如果没有相同任务块，使用默认容量
            deviceCapacities[deviceName] = this.getDefaultDeviceCapacity(deviceName)
          }
        }
      })
      
      return deviceCapacities
    },

    // 初始化设备容量配置
    initializeDeviceCapacities() {
      if (this.taskBlockEditor) {
        this.taskBlockEditor.initializeDeviceCapacities()
      }
    },

    // 更新设备容量（影响同一卫星中相同任务块名称的设备容量）
    async updateDeviceCapacity(deviceName, value) {
      if (this.taskBlockEditor) {
        await this.taskBlockEditor.updateDeviceCapacity(deviceName, value)
      }
      
      // 自动触发冲突检测刷新
      this.$nextTick(() => {
        this.detectDeviceConflicts();
      });
    },

    // 激活某一天的设备（自动填入总体所需设备）
    async activateDayDevices(dayIndex) {
      if (this.editingItem.requiredDevices.length === 0) {
        alert('请先选择任务所需的设备')
        return
      }
      
      // 将总体所需设备复制到指定天的配置中
      this.editingItem.dayConfigs[dayIndex] = [...this.editingItem.requiredDevices]
      
      // 更新实际的scheduleItems中的数据并自动保存
      const itemIndex = this.scheduleItems.findIndex(item => item.id === this.editingItem.id);
      if (itemIndex !== -1) {
        this.scheduleItems[itemIndex].dayConfigs = [...this.editingItem.dayConfigs];
        // await this.saveScheduleData();
      }
    },

    // 移除某一天的某个设备
    async removeDayDevice(dayIndex, deviceName) {
      if (this.editingItem.dayConfigs[dayIndex]) {
        const deviceIndex = this.editingItem.dayConfigs[dayIndex].indexOf(deviceName)
        if (deviceIndex > -1) {
          this.editingItem.dayConfigs[dayIndex].splice(deviceIndex, 1)
          
          // 更新实际的scheduleItems中的数据并自动保存
          const itemIndex = this.scheduleItems.findIndex(item => item.id === this.editingItem.id);
          if (itemIndex !== -1) {
            this.scheduleItems[itemIndex].dayConfigs = [...this.editingItem.dayConfigs];
            // await this.saveScheduleData();
          }
        }
      }
    },

    // 激活所有天数的设备（自动填入总体所需设备）
    async activateAllDaysDevices() {
      if (this.editingItem.requiredDevices.length === 0) {
        alert('请先选择任务所需的设备')
        return
      }

      // 为所有天数激活总体所需设备
      for (let i = 0; i < this.editingItem.duration; i++) {
        this.editingItem.dayConfigs[i] = [...this.editingItem.requiredDevices]
      }
      
      // 更新实际的scheduleItems中的数据并自动保存
      const itemIndex = this.scheduleItems.findIndex(item => item.id === this.editingItem.id);
      if (itemIndex !== -1) {
        this.scheduleItems[itemIndex].dayConfigs = [...this.editingItem.dayConfigs];
        // await this.saveScheduleData();
      }
    },

    // 清除所有天数的设备配置
    async clearAllDaysDevices() {
      // 为所有天数清空设备配置
      for (let i = 0; i < this.editingItem.duration; i++) {
        this.editingItem.dayConfigs[i] = []
      }
      
      // 更新实际的scheduleItems中的数据并自动保存
      const itemIndex = this.scheduleItems.findIndex(item => item.id === this.editingItem.id);
      if (itemIndex !== -1) {
        this.scheduleItems[itemIndex].dayConfigs = [...this.editingItem.dayConfigs];
        // await this.saveScheduleData();
      }
    },
    
    // 多选相关方法
    isItemSelected(itemId) {
      return this.selectedItems.some(item => item.id === itemId)
    },
    
    // 获取当前任务块及其后续所有任务块（用于Shift+拖拽联动）
    getTaskAndSubsequentTasks(currentTask, satelliteId) {
      // 获取同一卫星上的所有任务块
      const satelliteTasks = this.scheduleItems.filter(item => item.satelliteId === satelliteId)
      
      // 按startDay排序
      satelliteTasks.sort((a, b) => a.startDay - b.startDay)
      
      // 找到当前任务块的索引
      const currentTaskIndex = satelliteTasks.findIndex(task => task.id === currentTask.id)
      
      if (currentTaskIndex === -1) {
        return [currentTask] // 如果找不到，只返回当前任务块
      }
      
      // 返回当前任务块及其后续所有任务块
      return satelliteTasks.slice(currentTaskIndex)
    },
    
    handleItemClick(event, item) {
      if (this.mouseKeyboardOps) {
        this.mouseKeyboardOps.handleItemClick(event, item)
      }
    },
    
    handleItemDoubleClick(event, item) {
      if (this.mouseKeyboardOps) {
        this.mouseKeyboardOps.handleItemDoubleClick(event, item)
      }
    },
    
    clearSelection() {
      if (this.mouseKeyboardOps) {
        this.mouseKeyboardOps.clearSelection()
      }
    },
    
    // 框选相关方法
    onScheduleTableMouseDown(event) {
      if (this.mouseKeyboardOps) {
        this.mouseKeyboardOps.onScheduleTableMouseDown(event)
      }
    },
    
    onScheduleTableMouseMove(event) {
      if (this.mouseKeyboardOps) {
        this.mouseKeyboardOps.onScheduleTableMouseMove(event)
      }
    },
    
    onScheduleTableMouseUp(event) {
      if (this.mouseKeyboardOps) {
        this.mouseKeyboardOps.onScheduleTableMouseUp(event)
      }
    },
    
    onScheduleTableMouseLeave(event) {
      if (this.mouseKeyboardOps) {
        this.mouseKeyboardOps.onScheduleTableMouseLeave(event)
      }
    },
    
    updateSelectionBox() {
      const left = Math.min(this.selectionStart.x, this.selectionCurrent.x)
      const top = Math.min(this.selectionStart.y, this.selectionCurrent.y)
      const width = Math.abs(this.selectionCurrent.x - this.selectionStart.x)
      const height = Math.abs(this.selectionCurrent.y - this.selectionStart.y)
      
      this.selectionBox = { left, top, width, height }
    },
    
    updateSelectedItemsInBox() {
      if (!this.isSelecting) {
        console.log('updateSelectedItemsInBox: 不在框选状态，跳过')
        return
      }
      
      // 获取框选区域内的任务块
      const selectedInBox = []
      
      // 遍历所有任务块元素
      const taskElements = document.querySelectorAll('[data-task-id]')
      console.log('updateSelectedItemsInBox: 找到任务块元素数量', taskElements.length)
      
      if (taskElements.length === 0) {
        console.warn('updateSelectedItemsInBox: 没有找到任务块元素')
        return
      }
      
      taskElements.forEach(element => {
        const taskId = element.getAttribute('data-task-id')
        if (!taskId) {
          console.warn('updateSelectedItemsInBox: 元素缺少data-task-id属性', element)
          return
        }
        
        const task = this.scheduleItems.find(item => item.id == taskId)
        if (!task) {
          console.warn('updateSelectedItemsInBox: 找不到对应的任务块数据', taskId)
          return
        }
        
        if (this.isElementInSelectionBox(element)) {
          selectedInBox.push(task)
        }
      })
      
      console.log('updateSelectedItemsInBox: 框选区域内的任务块', {
        count: selectedInBox.length,
        taskIds: selectedInBox.map(t => t.id),
        isCtrlPressed: this.isCtrlPressed
      })
      
      // 如果按住Ctrl键，与现有选择合并；否则替换现有选择
      if (this.isCtrlPressed) {
        // 合并选择：添加新选中的，保留已选中的
        selectedInBox.forEach(task => {
          if (!this.isItemSelected(task.id)) {
            this.selectedItems.push(task)
          }
        })
      } else {
        // 替换选择
        this.selectedItems = [...selectedInBox]
      }
      
      console.log('updateSelectedItemsInBox: 最终选中的任务块数量', this.selectedItems.length)
    },
    
    isElementInSelectionBox(element) {
      if (!element) {
        console.warn('isElementInSelectionBox: 元素为空')
        return false
      }
      
      const elementRect = element.getBoundingClientRect()
      const tableElement = document.querySelector('.schedule-table')
      
      if (!tableElement) {
        console.error('isElementInSelectionBox: 找不到表格容器元素')
        return false
      }
      
      const tableRect = tableElement.getBoundingClientRect()
      
      // 转换为相对于表格的坐标，考虑表格内部滚动
      const elementRelative = {
        left: elementRect.left - tableRect.left + tableElement.scrollLeft,
        top: elementRect.top - tableRect.top + tableElement.scrollTop,
        right: elementRect.right - tableRect.left + tableElement.scrollLeft,
        bottom: elementRect.bottom - tableRect.top + tableElement.scrollTop
      }
      
      const boxRight = this.selectionBox.left + this.selectionBox.width
      const boxBottom = this.selectionBox.top + this.selectionBox.height
      
      // 检查是否有重叠
      const isOverlapping = !(elementRelative.right < this.selectionBox.left || 
               elementRelative.left > boxRight ||
               elementRelative.bottom < this.selectionBox.top || 
               elementRelative.top > boxBottom)
      
      // 调试日志
      if (isOverlapping) {
        const taskId = element.getAttribute('data-task-id')
        console.log('任务块在框选区域内', {
          taskId,
          elementRelative,
          selectionBox: this.selectionBox,
          boxRight,
          boxBottom
        })
      }
      
      return isOverlapping
    },
    
    // 检查指定时间槽是否被任务块占用
    isCellOccupied(satelliteId, timeIndex) {
      return this.scheduleItems.some(item => {
        return item.satelliteId === satelliteId && 
               timeIndex >= item.startDay && 
               timeIndex < item.startDay + item.duration
      })
    },

    // 验证没有任务块重叠（开发调试用）
    validateNoOverlap(satelliteId) {
      return this.conflictDetection.validateNoOverlap(satelliteId)
    },

    // 获取设备容量
    getDeviceCapacity(deviceName) {
      return this.conflictDetection.getDeviceCapacity(deviceName)
    },

    // 从同一卫星的相同任务块名称中获取设备容量
    getDeviceCapacityFromSameTaskName(satelliteId, taskName, deviceName) {
      // 查找同一卫星中相同任务块名称的任务块
      for (const task of this.scheduleItems) {
        if (task.satelliteId === satelliteId && 
            task.title === taskName && 
            task.deviceCapacities && 
            task.deviceCapacities[deviceName] !== undefined) {
          const capacity = task.deviceCapacities[deviceName]
          return capacity
        }
      }
      return null // 没有在同一卫星的相同任务块中找到该设备的容量配置
    },

    // 从任务块中获取设备容量配置（不再同步到其他任务块）
    getDeviceCapacityFromTasks(deviceName) {
      return this.conflictDetection.getDeviceCapacityFromTasks(deviceName)
    },

    // 获取指定日期内所有任务块中该设备的最小容量作为冲突判定标准
    getMinDeviceCapacityForDate(deviceName, records) {
      return this.conflictDetection.getMinDeviceCapacityForDate(deviceName, records)
    },

    // 同步相同设备在所有任务块中的容量配置
    synchronizeDeviceCapacity(deviceName, capacity) {
      let hasChanges = false
      
      this.scheduleItems.forEach(task => {
        if (task.deviceCapacities && task.deviceCapacities[deviceName] !== undefined) {
          if (task.deviceCapacities[deviceName] !== capacity) {
            task.deviceCapacities[deviceName] = capacity
            hasChanges = true
          }
        }
      })
      
      // 如果有容量更改，保存数据
       if (hasChanges) {
         // this.saveScheduleData() // 自动保存已注释
       }
    },

    // 同步同一卫星中相同任务块名称的设备容量
    synchronizeDeviceCapacityBySameTaskName(satelliteId, taskName, deviceName, capacity) {
      let hasChanges = false
      
      this.scheduleItems.forEach(task => {
        // 只同步同一卫星中相同任务块名称的任务块
        if (task.satelliteId === satelliteId && 
            task.title === taskName && 
            task.deviceCapacities && 
            task.deviceCapacities[deviceName] !== undefined) {
          if (task.deviceCapacities[deviceName] !== capacity) {
            task.deviceCapacities[deviceName] = capacity
            hasChanges = true
          }
        }
      })
      
      // 如果有容量更改，保存数据
       if (hasChanges) {
         // this.saveScheduleData() // 自动保存已注释
       }
    },

    // 判断设备是否是真空热试验罐（需要特殊的时间同步处理）
    // 计算任务块中每个设备的具体使用日期
    calculateDeviceUsageDates(task, baseDate) {
      return this.conflictDetection.calculateDeviceUsageDates(task, baseDate)
    },

    // 检测设备使用冲突
    detectDeviceConflicts(satelliteId = null) {
      return this.conflictDetection.detectDeviceConflicts(satelliteId)
    },

    // 自动排列后续任务块
    autoArrangeSubsequentTasks(satelliteId, insertPosition, insertedDuration, excludeIds = []) {
      // 开始自动排列后续任务块
      
      // 获取该卫星的所有任务块（排除被拖拽的任务块）
      let allTasks = this.scheduleItems
        .filter(item => item.satelliteId === satelliteId && !excludeIds.includes(item.id))
        .sort((a, b) => a.startDay - b.startDay)

      const insertEndPosition = insertPosition + insertedDuration
      // 计算插入区域结束位置
      
      // 找到所有需要移动的任务块
      // 包括：1) 与插入区域重叠的任务块，2) 在插入区域结束位置之后的任务块
      const tasksToMove = []
      
      allTasks.forEach(task => {
        const taskEnd = task.startDay + task.duration
        
        // 简化逻辑：任何与插入区域有交集或在插入区域之后的任务块都需要重新排列
        const needsRearrangement = (
          // 任务块与插入区域有重叠
          (task.startDay < insertEndPosition && taskEnd > insertPosition) ||
          // 任务块完全在插入区域之后
          (task.startDay >= insertEndPosition)
        )
        
        if (needsRearrangement) {
          tasksToMove.push(task)
        }
      })

      if (tasksToMove.length === 0) {
        // 没有需要移动的任务块
        return
      }

      // 按开始时间排序，确保按顺序处理
      const sortedTasksToMove = tasksToMove.sort((a, b) => a.startDay - b.startDay)
      
      // 计算需要移动的任务块之间的原有间隔
      const originalIntervals = []
      for (let i = 0; i < sortedTasksToMove.length - 1; i++) {
        const currentTask = sortedTasksToMove[i]
        const nextTask = sortedTasksToMove[i + 1]
        const interval = nextTask.startDay - (currentTask.startDay + currentTask.duration)
        originalIntervals.push(Math.max(0, interval)) // 确保间隔不为负数
      }
      
      // 从插入区域的结束位置开始，重新安排所有受影响的任务块
      let currentPosition = insertEndPosition
      
      sortedTasksToMove.forEach((taskToMove, index) => {
        const itemIndex = this.scheduleItems.findIndex(item => item.id === taskToMove.id)
        if (itemIndex > -1) {
          const taskEnd = taskToMove.startDay + taskToMove.duration
          
          // 判断是否需要移动任务块
          let newPosition = taskToMove.startDay
          
          // 如果任务块与插入区域有任何重叠或冲突，都需要移动
          const hasAnyConflict = taskToMove.startDay < insertEndPosition && taskEnd > insertPosition
          const needsRepositioning = taskToMove.startDay < currentPosition
          
          if (hasAnyConflict || needsRepositioning) {
            newPosition = currentPosition
          } else {
            newPosition = taskToMove.startDay
          }
          
          // 更新任务块位置
          if (newPosition !== taskToMove.startDay) {
            // 重新计算actualStartDate
            const newActualStartDate = this.parseYMDLocal(this.startDate)
            newActualStartDate.setDate(newActualStartDate.getDate() + newPosition)
            
            this.scheduleItems[itemIndex] = {
              ...this.scheduleItems[itemIndex],
              startDay: newPosition,
              actualStartDate: this.toYMDLocal(newActualStartDate)
            }
          }
          
          // 更新下一个可用位置，保持原有间隔
          currentPosition = newPosition + taskToMove.duration
          if (index < originalIntervals.length) {
            currentPosition += originalIntervals[index]
          }
        }
      })
      
      // 自动排列完成
    },
    
    handleCellClick(event, satelliteId, timeIndex) {
      // 如果点击的是空白区域（不是任务块），清空选中状态
      if (event.target.classList.contains('schedule-cell') || event.target.classList.contains('schedule-content')) {
        // 只有在单元格未被占用时才清空选中状态
        if (!this.isCellOccupied(satelliteId, timeIndex)) {
          this.clearSelection()
        }
      }
    },
    
    // 键盘事件处理
    async handleKeyDown(event) {
      if (this.mouseKeyboardOps) {
        await this.mouseKeyboardOps.handleKeyDown(event)
      }
    },
    
    handleKeyUp(event) {
      if (this.mouseKeyboardOps) {
        this.mouseKeyboardOps.handleKeyUp(event)
      }
    },
    
    // 水平移动选中的任务块（日期移动）
    async moveSelectedTasksHorizontally(direction) {
      if (this.selectedItems.length === 0) {
        console.log('没有选中的任务块')
        return
      }
      
      // 预检查：检查关键任务块是否有冲突
      let criticalItem
      if (direction === 'backward') {
        // W键移动时，检查最开始的任务块（startDay最小的）
        criticalItem = this.selectedItems.reduce((earliest, current) => 
          current.startDay < earliest.startDay ? current : earliest
        )
      } else {
        // S键移动时，检查最末尾的任务块（startDay + duration最大的）
        criticalItem = this.selectedItems.reduce((latest, current) => 
          (current.startDay + current.duration) > (latest.startDay + latest.duration) ? current : latest
        )
      }
      
      // 计算关键任务块的目标位置
      let criticalTargetStartDay
      if (direction === 'backward') {
        criticalTargetStartDay = criticalItem.startDay - 1
      } else {
        criticalTargetStartDay = criticalItem.startDay + 1
      }
      
      // 检查关键任务块的目标位置是否有效
      if (criticalTargetStartDay < 1) {
        console.log(`移动失败：最开始的任务块 ${criticalItem.title} 已到达最早日期`)
        return
      }
      
      // 检查关键任务块的目标位置是否有冲突
      const selectedItemIds = this.selectedItems.map(item => item.id)
      const criticalHasConflict = this.checkTaskConflictAtPosition(
        criticalItem.satelliteId,
        criticalTargetStartDay,
        criticalItem.duration,
        selectedItemIds
      )
      
      if (criticalHasConflict) {
        console.log(`移动失败：关键任务块 ${criticalItem.title} 在目标位置有冲突`)
        return
      }
      
      // 保存当前状态到历史记录
      this.saveToHistory()
      
      let moveCount = 0
      let failCount = 0
      
      // 第一步：收集所有任务块的移动计划
      const movePlans = []
      
      for (const selectedItem of this.selectedItems) {
        // 计算目标日期
        let targetStartDay
        if (direction === 'backward') {
          targetStartDay = selectedItem.startDay - 1
        } else { // forward
          targetStartDay = selectedItem.startDay + 1
        }
        
        // 检查目标日期是否有效（不能小于1）
        if (targetStartDay < 1) {
          console.log(`任务块 ${selectedItem.title} 无法向前移动：已到达最早日期`)
          failCount++
          continue
        }
        
        // 检查目标位置是否有冲突（排除所有选中的任务块）
        const hasConflict = this.checkTaskConflictAtPosition(
          selectedItem.satelliteId,
          targetStartDay,
          selectedItem.duration,
          selectedItemIds // 排除所有选中的任务块
        )
        
        if (hasConflict) {
          console.log(`任务块 ${selectedItem.title} 无法移动到第${targetStartDay}天：目标位置有冲突`)
          failCount++
          continue
        }
        
        // 添加到移动计划
        movePlans.push({
          item: selectedItem,
          targetStartDay: targetStartDay
        })
      }
      
      // 第二步：统一执行所有移动操作
      for (const plan of movePlans) {
        const itemIndex = this.scheduleItems.findIndex(item => item.id === plan.item.id)
        if (itemIndex > -1) {
          this.scheduleItems[itemIndex] = {
            ...this.scheduleItems[itemIndex],
            startDay: plan.targetStartDay
          }
          
          // 更新选中项目中的startDay
          plan.item.startDay = plan.targetStartDay
          
          moveCount++
          console.log(`任务块 ${plan.item.title} 成功移动到第${plan.targetStartDay}天`)
        }
      }
      
      // 显示移动结果
      if (moveCount > 0) {
        // 重新检测冲突
        const conflicts = this.detectDeviceConflicts()
        if (conflicts.length > 0) {
          this.markConflictingTasks(conflicts)
        } else {
          this.clearConflictMarks()
        }
        
        // 强制更新视图
        this.$forceUpdate()
      }
      
      if (failCount > 0) {
        const message = moveCount > 0 
          ? `${moveCount} 个任务块日期移动成功，${failCount} 个任务块移动失败（目标日期有冲突或已到达边界）`
          : `${failCount} 个任务块移动失败（目标日期有冲突或已到达边界）`
        
        // 显示提示信息
        this.showMoveResultMessage(message)
      }
    },
    
    // 检查指定位置是否有任务块冲突
    checkTaskConflictAtPosition(satelliteId, startDay, duration, excludeIds = []) {
      const endDay = startDay + duration
      
      return this.scheduleItems.some(item => {
        // 排除指定的任务块ID
        if (excludeIds.includes(item.id)) {
          return false
        }
        
        // 检查是否在同一卫星
        if (item.satelliteId !== satelliteId) {
          return false
        }
        
        // 检查时间是否重叠
        const itemEndDay = item.startDay + item.duration
        return !(endDay <= item.startDay || startDay >= itemEndDay)
      })
    },
    
    // 显示移动结果消息
    showMoveResultMessage(message) {
      // 创建临时提示元素
      const messageEl = document.createElement('div')
      messageEl.textContent = message
      messageEl.style.cssText = `
        position: fixed;
        top: 20px;
        left: 50%;
        transform: translateX(-50%);
        background: #333;
        color: white;
        padding: 10px 20px;
        border-radius: 4px;
        z-index: 10000;
        font-size: 14px;
        box-shadow: 0 2px 8px rgba(0,0,0,0.2);
      `
      
      document.body.appendChild(messageEl)
      
      // 3秒后自动移除
      setTimeout(() => {
        if (messageEl.parentNode) {
          messageEl.parentNode.removeChild(messageEl)
        }
      }, 3000)
    },
    
    // 删除选中的任务块
    async deleteSelectedItems() {
      if (this.selectedItems.length === 0) {
        return
      }
      
      const itemCount = this.selectedItems.length
      const confirmMessage = itemCount === 1 
        ? `确定要删除任务块"${this.selectedItems[0].title}"吗？`
        : `确定要删除选中的${itemCount}个任务块吗？`
      
      if (confirm(confirmMessage)) {
        // 在删除前保存历史记录
        this.saveToHistory()
        
        // 获取要删除的任务块ID列表和卫星ID列表
        const idsToDelete = this.selectedItems.map(item => item.id)
        const satellitesWithDeletedItems = [...new Set(this.selectedItems.map(item => item.satelliteId))]
        
        // 设置标志防止watcher重复保存
        this.isUpdatingScheduleItems = true
        
        // 从scheduleItems中移除选中的任务块
        this.scheduleItems = this.scheduleItems.filter(item => !idsToDelete.includes(item.id))
        
        // 重置标志
        this.isUpdatingScheduleItems = false
        
        // 检查每个受影响的卫星是否还有剩余任务块
        for (const satelliteId of satellitesWithDeletedItems) {
          const remainingSatelliteItems = this.scheduleItems.filter(item => item.satelliteId === satelliteId)
          
          // 如果该卫星没有其他任务块了，删除数据库中的整条记录
          if (remainingSatelliteItems.length === 0) {

            await this.deleteSatelliteScheduleRecord(satelliteId)
          }
        }
        
        // 清空选中状态
        this.clearSelection()
        
        // 如果没有任务块了，更新状态
        if (this.scheduleItems.length === 0) {
          this.hasScheduleData = false
          this.clearConflictMarks()
        } else {
          // 删除任务块后重新检测冲突
          const conflicts = this.detectDeviceConflicts()
          if (conflicts.length > 0) {
            this.markConflictingTasks(conflicts)

          } else {
            this.clearConflictMarks()

          }
        }
        
        // 已移除自动保存
      }
    },
    
    // 标记冲突的任务块
    markConflictingTasks(conflicts) {
      this.conflictDetection.markConflictingTasks(conflicts)
    },
    
    // 清除所有冲突标记
    clearConflictMarks() {
      this.conflictDetection.clearConflictMarks()
    },
  },
  
  watch: {
    // 监听导航数据变化，更新表格显示状态
    navigationData: {
      handler() {
        // 当导航数据变化时，重新检查是否应该显示表格
        const shouldShowData = this.scheduleItems.length > 0 || this.allSatellites.length > 0

        this.hasScheduleData = shouldShowData
      },
      deep: true,
      immediate: false
    },
    
    // 监听startDate变化，调整排程数据的相对位置（使用本地日期差）
    startDate(newDate, oldDate) {
      // 在对齐起始日期过程中，跳过相对位移，避免双重调整
      if (this.isAligningStartDate) {
        return
      }
      if (oldDate && newDate !== oldDate && this.scheduleItems.length > 0) {
        // 使用统一的本地日期差计算，避免时区偏移
        const dayDiff = this.dateDiffInDaysLocal(newDate, oldDate)

        // 调整所有排程项目的startDay（允许负数，表示在当前日期范围之前）
        const updatedItems = this.scheduleItems.map(item => ({
          ...item,
          startDay: item.startDay - dayDiff
        }))

        // 替换整个数组以触发响应式更新
        this.scheduleItems = updatedItems
      }
    },
    
    // 监听scheduleItems变化（已移除自动保存）
    scheduleItems: {
      handler() {
        // 不再自动保存，只更新显示状态
        this.hasScheduleData = this.scheduleItems.length > 0 || this.allSatellites.length > 0
      },
      deep: true
    },
    
    // 监听任务块标题变化，重新初始化设备容量
    async 'editingItem.title'(newTitle, oldTitle) {
      if (newTitle && newTitle !== oldTitle) {
        if (this.editingItem.requiredDevices.length > 0) {
          // 当任务块标题改变时，重新初始化设备容量
          // 为已选择的设备重新从同一卫星的相同任务块名称中获取容量
          this.editingItem.requiredDevices.forEach(deviceName => {
            const sameTaskCapacity = this.getDeviceCapacityFromSameTaskName(this.editingItem.satelliteId, newTitle, deviceName)
            if (sameTaskCapacity !== null) {
              this.editingItem.deviceCapacities[deviceName] = sameTaskCapacity
              // 设备容量已更新
            }
            // 如果没有找到相同任务块的容量配置，保持当前容量不变
          })
        }
        
        // 更新实际的scheduleItems中的数据并自动保存
        const itemIndex = this.scheduleItems.findIndex(item => item.id === this.editingItem.id);
        if (itemIndex !== -1) {
          this.scheduleItems[itemIndex].title = newTitle;
          // await this.saveScheduleData(); // 自动保存已注释
        }
      }
    },
    
    // 监听编辑项的持续时间变化，自动调整每日配置数组
    async 'editingItem.duration'(newDuration, oldDuration) {
      if (newDuration && newDuration > 0) {
        // 调整dayConfigs数组长度，每个元素都是数组
        while (this.editingItem.dayConfigs.length < newDuration) {
          this.editingItem.dayConfigs.push([])
        }
        if (this.editingItem.dayConfigs.length > newDuration) {
          this.editingItem.dayConfigs = this.editingItem.dayConfigs.slice(0, newDuration)
        }
        // 确保所有元素都是数组
        this.editingItem.dayConfigs = this.editingItem.dayConfigs.map(dayConfig => 
          Array.isArray(dayConfig) ? dayConfig : (dayConfig ? [dayConfig] : [])
        )
      }
    }
  },
  
  async mounted() {
    try {
      injectSatelliteSchedulingStyles()
      // 初始化右键菜单操作模块
      this.contextMenuOps = new ContextMenuOperations(this)
      
      // 初始化任务块编辑器模块
      this.taskBlockEditor = new TaskBlockEditor(this)
      
      // 初始化鼠标键盘操作模块
      this.mouseKeyboardOps = new MouseKeyboardOperations(this)
      
      // 初始化冲突检测模块
      this.conflictDetection = new ConflictDetection(this)

      // 初始化模板管理模块
      this.templateManager = new TemplateManager(this)
      
      // 最高优先级：首先加载导航数据，这是界面显示的基础
      await this.loadNavigationData()

      
      // 导航数据加载完成后，并行加载其他数据以提高性能
      await Promise.all([
        this.loadAvailableDevices(), // 从API加载设备数据
        this.loadScheduleData(), // 从数据库加载排程数据
        this.loadTaskBlocks() // 加载任务块数据
      ])
      

      
      // 使用 $nextTick 确保所有响应式数据都已更新
      await this.$nextTick()
      
      // 最终检查并设置表格显示状态
      const shouldShowData = this.scheduleItems.length > 0 || this.allSatellites.length > 0

      this.hasScheduleData = shouldShowData
      
      // 在所有数据加载完成后，更新起始日期为所有任务块的最早时间
      // 移除自动对齐为最早任务日期，保持后端startDate为准
    } catch (error) {

      // 即使其他数据加载失败，也要确保导航数据已加载
      if (this.navigationData.length === 0) {

        try {
          await this.loadNavigationData()
        } catch (retryError) {

        }
      }
    }
  },
  
  beforeDestroy() {
    // 键盘事件监听器已在mouseKeyboardOps中处理，无需重复移除
  }
}
</script>